﻿using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebApp.Entities;
using WebApp.Models;

namespace WebApp.Services
{
    /// <summary>
    /// 
    /// </summary>
    public interface IPowerDataService
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        ApplicationDto GetApplication(int id);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="op"></param>
        void AddOperation(OperationDto op);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="record"></param>
        void AddMeterRecord(MeterRecordDto record);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        List<MeterDto> GetMeters(int pageSize, int pageIndex);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="interval"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        List<CollectorDto> GetCollectors(int interval, int pageSize, int pageIndex);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        void UpdateCollector(long id);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="power"></param>
        void UpdateMeter(long id, decimal power);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="log"></param>
        void AddExceptionLog(ExceptionLogDto log);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="limit"></param>
        /// <returns></returns>
        List<OperationDto> GetOperations(int limit);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        MeterDto GetMeter(string code);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="opId"></param>
        /// <param name="status"></param>
        void UpdateOperation(long opId, IoTCmdStatus status);
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        DateTime GetNearestTime();
        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryId"></param>
        /// <returns></returns>
        OperationDto GetOperation(string queryId);
    }
    /// <summary>
    /// 
    /// </summary>
    public class PowerDataService : IPowerDataService
    {
        PowerContext _db;
        private static ILogger<PowerDataService> _logger;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        /// <param name="logger"></param>
        public PowerDataService(PowerContext db, ILogger<PowerDataService> logger)
        {
            _db = db;

            _logger = logger;
        }

        /// <summary>
        /// 添加记录
        /// </summary>
        /// <param name="record"></param>
        public void AddMeterRecord(MeterRecordDto record)
        {
            try
            {
                _db.MeterRecords.Add(new MeterRecord
                {
                    ActivePower = record.ActivePower,
                    AppId = record.AppId,
                    CollectorCode = record.CollectorCode,
                    MeterCode = record.MeterCode,
                    TakeTime = record.TakeTime
                });
                _db.SaveChanges();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "ef异常");
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="op"></param>
        public void AddOperation(OperationDto op)
        {
            try
            {
                var entity = new Operation
                {
                    QueryId = op.QueryId,
                    Command = op.Command,
                    AppId = op.AppId,
                    CallbackUrl = op.CallbackUrl,
                    CollectorCode = op.CollectorCode,
                    CreateTime = DateTime.Now,
                    ExcuteTime = DateTime.Now,
                    MeterCode = op.MeterCode,
                    Password = op.Password,
                    RequestId = op.RequestId,
                    Status = op.Status,
                    TryCount = op.TryCount

                };
                _db.Operations.Add(entity);
                _db.SaveChanges();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "AddOp");
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public ApplicationDto GetApplication(int id)
        {
            try
            {
                var app = _db.Applications.Where(p => p.Id == id).Select(p => new ApplicationDto
                {
                    Id = p.Id,
                    AppName = p.AppName,
                    AppToken = p.AppToken
                }).FirstOrDefault();

                return app;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "ef异常");
                return new ApplicationDto();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        public List<MeterDto> GetMeters(int pageSize, int pageIndex)
        {
            return _db.Meters.Select(p => new MeterDto
            {
                AppId = p.AppId,
                AppToken = p.App.AppToken,
                //CallbackUrl = p.App.CallbackUrl,
                Subscribes = p.App.Subscribes.Select(s => new SubscribeDto
                {
                    AppId = s.AppId,
                    CallbackUrl = s.CallbackUrl,
                    NotifyType = s.NotifyType,
                    Id = s.Id
                }),
                CollectorCode = p.Collector.Code,
                MeterCode = p.Code,
                Last_Take_Date = p.Last_Take_Date,
                Status = p.Status
            }).Skip(pageIndex * pageSize).Take(pageSize).ToList();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public MeterDto GetMeter(string code)
        {
            return _db.Meters.Where(p => p.Code == code).Select(p => new MeterDto
            {
                Id = p.Id,
                AppId = p.AppId,
                AppToken = p.App.AppToken,
                Password = p.Password,
                CollectorCode = p.Collector.Code,
                MeterCode = p.Code,
                Status = p.Status
            }).FirstOrDefault();
        }
        /// <summary>
        /// 时间间隔最近的一个电表
        /// 判断还需要等待多久
        /// </summary>
        /// <returns></returns>
        public DateTime GetNearestTime()
        {
            return _db.Meters.Where(p => p.Collector.Status == 1 && p.Status == 1).Min(p => p.Last_Take_Date);
        }
        /// <summary>
        /// 待读表的采集器
        /// </summary>
        /// <param name="interval">间隔时间 分钟</param>
        /// <param name="pageSize">一次采集器数量</param>
        /// <param name="pageIndex">第几页</param>
        /// <returns></returns>
        public List<CollectorDto> GetCollectors(int interval, int pageSize, int pageIndex)
        {
            try
            {
                var data = _db.Collectors.AsNoTracking().Include(p => p.Meters).Include(p => p.App.Subscribes)
                    .Where(p => p.Status == 1)
                    .Skip(pageIndex * pageSize).Take(pageSize).ToList();
                return data.Select(p => new CollectorDto
                {
                    Id = p.Id,
                    AppId = p.AppId,
                    AppToken = p.App.AppToken,
                    //CallbackUrl = p.App.CallbackUrl,
                    CollectorCode = p.Code,
                    Meters = p.Meters.Where(m => m.Status == 1 && m.Last_Take_Date <= DateTime.Now.AddMinutes(0 - interval)).Select(c => new MeterDto
                    {
                        Id = c.Id,
                        CollectorCode = p.Code,
                        MeterCode = c.Code,
                        Password = c.Password,
                        AppId = p.AppId,
                        AppToken = p.App.AppToken,
                        Last_Take_Date = c.Last_Take_Date,
                        Status = c.Status,
                        //CallbackUrl = p.App.CallbackUrl
                        Subscribes = p.App.Subscribes.Select(s => new SubscribeDto
                        {
                            AppId = s.AppId,
                            CallbackUrl = s.CallbackUrl,
                            NotifyType = s.NotifyType,
                            Id = s.Id
                        }),
                    }).ToList()
                }).ToList().Where(p => p.Meters.Count > 0).ToList();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "GetCollectors");
                return new List<CollectorDto>();
            }
        }
        /// <summary>
        /// 更新最新电量
        /// </summary>
        /// <param name="id"></param>
        /// <param name="power"></param>
        public void UpdateMeter(long id, decimal power)
        {
            try
            {
                var mb = _db.Meters.FirstOrDefault(p => p.Id == id);
                if (mb != null)
                {
                    mb.Last_Take_Date = DateTime.Now;
                    if (power > 0)
                        mb.Last_Active_Power = power;

                    _db.Meters.Update(mb);
                    _db.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "ef异常");
            }
        }
        /// <summary>
        /// 更新最后执行时间
        /// </summary>
        /// <param name="id"></param>
        public void UpdateCollector(long id)
        {
            try
            {
                var mb = _db.Collectors.FirstOrDefault(p => p.Id == id);
                if (mb != null)
                {
                    mb.HeartbeatTime = DateTime.Now;

                    _db.Collectors.Update(mb);
                    _db.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "ef异常");
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryId"></param>
        /// <returns></returns>
        public OperationDto GetOperation(string queryId)
        {
            return _db.Operations.Where(p => p.QueryId == queryId).Select(p => new OperationDto
            {
                Id = p.Id,
                AppId = p.AppId,
                AppToken = p.App.AppToken,
                CallbackUrl = p.CallbackUrl,
                CollectorCode = p.CollectorCode,
                Command = p.Command,
                MeterCode = p.MeterCode,
                Password = p.Password,
                RequestId = p.RequestId,
                Status = p.Status,
                TryCount = p.TryCount,
                Subscribes = p.App.Subscribes.Select(s => new SubscribeDto
                {
                    AppId = s.AppId,
                    CallbackUrl = s.CallbackUrl,
                    NotifyType = s.NotifyType,
                    Id = s.Id
                })

            }).FirstOrDefault();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="limit"></param>
        /// <returns></returns>
        public List<OperationDto> GetOperations(int limit)
        {
            try
            {
                var oplist = _db.Operations.Where(p => p.Status == IoTCmdStatus.UnSend && p.TryCount < 2)
                            .Select(p => new OperationDto
                            {
                                Id = p.Id,
                                AppId = p.AppId,
                                AppToken = p.App.AppToken,
                                CallbackUrl = p.CallbackUrl,
                                CollectorCode = p.CollectorCode,
                                Command = p.Command,
                                MeterCode = p.MeterCode,
                                Password = p.Password,
                                RequestId = p.RequestId,
                                Status = p.Status,
                                TryCount = p.TryCount,
                                Subscribes = p.App.Subscribes.Select(s => new SubscribeDto
                                {
                                    AppId = s.AppId,
                                    CallbackUrl = s.CallbackUrl,
                                    NotifyType = s.NotifyType,
                                    Id = s.Id
                                })

                            }).Take(limit).ToList();

                return oplist;
            }
            catch (Exception e)
            {
                _logger.LogError(e, "GetOperations");
            }

            return default(List<OperationDto>);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="opId"></param>
        /// <param name="status"></param>
        public void UpdateOperation(long opId, IoTCmdStatus status)
        {
            try
            {
                var op = _db.Operations.FirstOrDefault(p => p.Id == opId);
                if (op != null)
                {
                    switch (status)
                    {
                        case IoTCmdStatus.FAILED:
                        case IoTCmdStatus.UnSend:
                            op.Status = op.TryCount >= 2 ? IoTCmdStatus.FAILED : IoTCmdStatus.UnSend;
                            op.TryCount++;
                            break;
                        case IoTCmdStatus.SUCCESSFUL:
                            op.Status = IoTCmdStatus.SUCCESSFUL;
                            break;
                    }
                    _db.Operations.Update(op);
                    _db.SaveChanges();
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "ef异常");
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="log"></param>
        public void AddExceptionLog(ExceptionLogDto log)
        {
            _db.ExceptionLogs.Add(new ExceptionLog
            {
                CollectorCode = log.CollectorCode,
                MeterCode = log.MeterCode,
                AppId = log.AppId,
                CallbackUrl = log.CallbackUrl,
                Command = log.Command,
                Message = log.Message.Substring(0, Math.Min(500, log.Message.Length)),
                RequestId = log.RequestId,
                StackTrace = log.StackTrace.Substring(0, Math.Min(1000, log.StackTrace.Length)),
                CreateTime = log.CreateTime
            });
            _db.SaveChanges();
        }
    }
}